#include <stdlib.h>
#include <assert.h>
#include "list.h"

typedef struct node_t {
	struct node_t *next;
    struct node_t *prev;
    list_element_t data;
} node;

int list_init(list *l)
{
    if (l != NULL) {
    	l->head = NULL;
        l->tail = NULL;
    	l->size = 0;
    	return LIST_SUCCESS;
    }

    return LIST_ERROR;
}

int list_get_size(list *l)
{
    return l != NULL ? l->size : LIST_ERROR;
}

int list_get_head(list *l, iterator *it)
{
    if (l != NULL && it != NULL) {
        it->n = l->head;
        return it->n == NULL ? LIST_END : LIST_SUCCESS;
    }

    return LIST_ERROR;
}

int list_get_tail(list *l, iterator *it)
{
    if (l != NULL && it != NULL) {
        it->n = l->tail;
        return it->n == NULL ? LIST_END : LIST_SUCCESS;
    }

    return LIST_ERROR;
}

int list_is_end(list *l, iterator *it)
{
    if (l != NULL && it != NULL) {
        return it->n == NULL ? 1 : 0;
    }

    return LIST_ERROR;
}

int list_get_next(list *l, iterator *it)
{
    if (l != NULL && it != NULL) {
        if (it->n != NULL) {
            it->n = it->n->next;
            return it->n != NULL ? LIST_SUCCESS : LIST_END;
        }
        else
        	/* CR are you sure it's not supposed to be LIST_ERROR? */
        	return LIST_END;
    }

    return LIST_ERROR;
}

int list_insert_before(list *l, iterator *it, list_element_t e)
{
	if (l != NULL && it != NULL) {
		node* p = (node*) malloc(sizeof(node));
		node* next = it->n;
		node* prev = next ? next->prev : NULL;

		if (p == NULL)
			return LIST_ERROR;

		p->data = e;
		p->next = next;

		if (next != NULL)
			next->prev = p;
		else if(l->tail == NULL) /* insert as tail */
			l->tail = p;
		else { /* insert as end */
			free(p);
			return LIST_END;
		}

		if (prev != NULL) {
			prev->next = p;
		}
		else
			l->head = p;

		p->prev = prev;
		l->size = l->size + 1;
		it->n = p;
		return LIST_SUCCESS;
	}

	return LIST_ERROR;
}

int list_insert_after(list *l, iterator *it, list_element_t e)
{
    if (l != NULL && it != NULL) {
        node* p = (node*) malloc(sizeof(node));
        node* prev = it->n;
        node* next = prev ? prev->next : NULL;

        if (p == NULL)
            return LIST_ERROR;

        p->data = e;
        p->next = next;

        if (next != NULL)
            next->prev = p;
        else
            l->tail = p;

        if (prev != NULL) {
            prev->next = p;
        }
        else if (l->head == NULL) /* insert as head */
            l->head = p;
        else { /* insert as end */
            free(p);
            return LIST_END;
        }

        p->prev = prev;
        l->size = l->size + 1;
		it->n = p;
        return LIST_SUCCESS;
    }

    return LIST_ERROR;
}

int list_insert_head(list *l, list_element_t e)
{
	iterator it;

	if (list_get_head(l, &it) != LIST_ERROR)
		return list_insert_before(l, &it, e);

	return LIST_ERROR;
}

int list_insert_tail(list *l, list_element_t e)
{
	iterator it;

    if (list_get_tail(l, &it) != LIST_ERROR)
        return list_insert_after(l, &it, e);

    return LIST_ERROR;
}

int list_remove(list *l, iterator *it)
{
    if (l != NULL && it != NULL) {
        if (it->n) {
            node* p = it->n;
            node* prev = p->prev;
            node* next = p->next;

            if (prev != NULL)
                prev->next = next;
            else
                l->head = next;

            if (next != NULL)
                next->prev = prev;
            else
                l->tail = prev;

            free(p);
            l->size = l->size - 1;
			it->n = next;
            return LIST_SUCCESS;
        }
        else
            return LIST_END;
    }

    return LIST_ERROR;
}

int list_remove_head(list *l)
{
    iterator it;

    if (list_get_head(l, &it) != LIST_ERROR)
        return list_remove(l, &it);

    return LIST_ERROR;
}

int list_remove_tail(list *l)
{
    iterator it;

    if (list_get_tail(l, &it) != LIST_ERROR)
        return list_remove(l, &it);

    return LIST_ERROR;
}

int list_find(list* l, list_element_t e, iterator *it)
{
    if (l != NULL && it != NULL) {
        LIST_FOREACH(l, it) {
            list_element_t tmp;
            if (iterator_get_value(it, &tmp) != LIST_SUCCESS)
                return LIST_ERROR;

            if (e == tmp)
                return LIST_SUCCESS;
        }

        return LIST_END;
    }

    return LIST_ERROR;
}

int list_destroy(list *l)
{
	if (l != NULL) {
		while (l->size > 0) {
			list_remove_tail(l);
		}

		return LIST_SUCCESS;
	}

	return LIST_ERROR;
}

int iterator_get_value(iterator *it, list_element_t *e)
{
    if (it != NULL && e != NULL) {
        if (it->n != NULL) {
            *e = it->n->data;
            return LIST_SUCCESS;
        }
        else
        	return LIST_END;
    }

    return LIST_ERROR;
}
